#include <stdio.h>
#include "esp_log.h"
#include "https_download2.h"
#include "SD_MMC.h"

#define TAG "HttpsDownload2"



esp_err_t HttpsDownload2::eventHandler(esp_http_client_event_t *evt)
{
    HttpsDownload2 *download = static_cast<HttpsDownload2 *>(evt->user_data);
    switch (evt->event_id)
    {
    case HTTP_EVENT_ERROR:
        ESP_LOGE(TAG, "HTTP_EVENT_ERROR");
        download->updateStatus(DownloadStatus::FAILED);
        break;
    case HTTP_EVENT_ON_CONNECTED:
        ESP_LOGI(TAG, "HTTP_EVENT_ON_CONNECTED");
        download->startTime = xTaskGetTickCount();
        download->updateStatus(DownloadStatus::DOWNLOADING);
        break;
    case HTTP_EVENT_HEADER_SENT:
        ESP_LOGI(TAG, "HTTP_EVENT_HEADER_SENT");
        break;
    case HTTP_EVENT_ON_HEADER:
        ESP_LOGI(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
        if (strcmp(evt->header_key, "Content-Length") == 0)
        {
            download->totalSize = atoi(evt->header_value);
            ESP_LOGI(TAG, "Total file size: %zu bytes", download->totalSize);
        }
        break;
    case HTTP_EVENT_ON_FINISH:
    {
        int status_code = esp_http_client_get_status_code(evt->client);
        if (status_code == 200)
        {
            ESP_LOGI(TAG, "HTTP_EVENT_ON_FINISH, Status Code: %d", status_code);
            if (download->file)
            {
                download->file.close();
            }
            // 检查是否下载完成
            if (download->downloadedSize == download->totalSize)
            {
                download->updateStatus(DownloadStatus::COMPLETED);
                float progress = download->getDownloadPercentage();
                ESP_LOGI(TAG, "[%s] Downloaded %.2f%% (%zu / %zu bytes), Average Speed: %.2f B/s",
                         download->info.filePath.c_str(), progress, download->downloadedSize, download->totalSize, download->getAverageDownloadSpeed());
            }
            else
            {
                download->updateStatus(DownloadStatus::FAILED);
            }
        }
        else
        {
            ESP_LOGE(TAG, "Invalid status code %d, download failed", status_code);
            if (download->file)
            {
                download->file.close();
                SD_MMC.remove(download->info.filePath.c_str());
            }
            download->updateStatus(DownloadStatus::FAILED);
        }
        break;
    }
    case HTTP_EVENT_DISCONNECTED:
        ESP_LOGI(TAG, "HTTP_EVENT_DISCONNECTED");
        if (download->file)
        {
            download->file.close();
        }
        // 判断是否下载完成
        if (download->downloadedSize != download->totalSize)
        {
            download->updateStatus(DownloadStatus::FAILED);
        }
        break;
    case HTTP_EVENT_ON_DATA:
    {
        int status_code = esp_http_client_get_status_code(evt->client);
        if (status_code == 200)
        {
            // ESP_LOGI(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
            if (!esp_http_client_is_chunked_response(evt->client))
            {
                if (download->file)
                {
                    download->file.write((const uint8_t *)evt->data, evt->data_len);
                    download->downloadedSize += evt->data_len;

                    // 计算并输出下载百分比进度
                    if (download->totalSize > 0)
                    {
                        float progress = download->getDownloadPercentage();
                        // ESP_LOGI(TAG, "[%s] Downloaded %.2f%% (%zu bytes out of %zu bytes), Average Speed: %.2f B/s",
                        //          download->info.filePath.c_str(), progress, download->downloadedSize, download->totalSize, download->getAverageDownloadSpeed());
                    }
                    download->updateSpeed();
                }
                else
                {
                    ESP_LOGE(TAG, "File pointer is null, cannot write data");
                    download->updateStatus(DownloadStatus::FAILED);
                }
            }
        }
        break;
    }
    default:
        break;
    }
    return ESP_OK;
}


void HttpsDownload2::downloadTask(void *pvParameters)
{
    HttpsDownload2 *downloadManager = static_cast<HttpsDownload2 *>(pvParameters);
    while (true)
    {
        if (downloadManager->downloadQueue.size())
        {
            ESP_LOGI(TAG, "list size:%d", downloadManager->downloadQueue.size());
            downloadManager->info = downloadManager->downloadQueue.shift();
            downloadManager->startTime = xTaskGetTickCount();
            downloadManager->downloadedSize = 0;
            downloadManager->totalSize = 0;
            downloadManager->updateStatus(DownloadStatus::DOWNLOADING);
            esp_err_t err = downloadManager->performDownload();
            if (err != ESP_OK)
            {
                ESP_LOGE(TAG, "Download failed for %s", downloadManager->info.url.c_str());
                downloadManager->updateStatus(DownloadStatus::FAILED);
            }
            else
            {
                downloadManager->updateStatus(DownloadStatus::COMPLETED);
            }
        }
        vTaskDelay(pdMS_TO_TICKS(100)); // 每隔100ms检查一次队列
    }
}



HttpsDownload2::HttpsDownload2()
{
    downloadTaskHandle = nullptr;
    status = DownloadStatus::IDLE;
    totalSize = 0;
    downloadedSize = 0;
    startTime = 0;
    averageSpeed = 0;
}

HttpsDownload2::~HttpsDownload2()
{
    if (downloadTaskHandle != nullptr)
    {
        vTaskDelete(downloadTaskHandle);
    }
}

void HttpsDownload2::begin()
{
    xTaskCreate(downloadTask, "DownloadTask", 10000, this, 5, &downloadTaskHandle);
}

void HttpsDownload2::add(HttpsDownloadInfo_t &info)
{
    downloadQueue.add(info);
    ESP_LOGI(TAG, "add file:%s", info.url.c_str());
}

esp_err_t HttpsDownload2::performDownload()
{
    esp_http_client_config_t config = {0};
    config.url = info.url.c_str();
    config.timeout_ms = 5000; // 5 秒超时

    // 根据 URL 和 certPem 判断是否为 HTTPS 协议
    if (info.url.startsWith("https://") && info.certPem != nullptr)
    {
        config.cert_pem = info.certPem;
    }
    else
    {
        config.cert_pem = nullptr;
    }

    // 调整缓冲区大小
    config.buffer_size = 1024;    // 增大接收缓冲区大小
    config.buffer_size_tx = 1024; // 增大发送缓冲区大小

    config.event_handler = eventHandler;
    config.user_data = this;

    esp_http_client_handle_t client = esp_http_client_init(&config);

    // 如果 info.filePath 为空，则将 info.url 的最后一个 / 后的文件名作为 filePath
    if (info.filePath.isEmpty())
    {
        int lastSlashIndex = info.url.lastIndexOf('/');
        if (lastSlashIndex != -1)
        {
            info.filePath = "/" + info.url.substring(lastSlashIndex + 1);
        }
    }
    ESP_LOGI(TAG, "info.filepath = %s", info.filePath.c_str());

    // 检查文件是否存在，若存在则删除
    if (SD_MMC.exists(info.filePath))
    {
        ESP_LOGI(TAG, "File already exists. Deleting and starting a fresh download...");
        if (!SD_MMC.remove(info.filePath))
        {
            ESP_LOGE(TAG, "Failed to delete the existing file.");
            updateStatus(DownloadStatus::FAILED);
            esp_http_client_cleanup(client);
            return ESP_FAIL;
        }
    }
    // 打开文件进行写入
    file = SD_MMC.open(info.filePath, FILE_WRITE);
    if (!file)
    {
        ESP_LOGE(TAG, "Failed to open file for writing");
        esp_http_client_cleanup(client);
        return ESP_FAIL;
    }

    esp_err_t err = esp_http_client_perform(client);
    esp_http_client_cleanup(client);    
    return err;
}

void HttpsDownload2::setEventCallback(DownloadEventCallback callback)
{
    eventCallback = callback;
}

void HttpsDownload2::updateStatus(DownloadStatus newStatus)
{
    status = newStatus;
    if (eventCallback != nullptr)
    {
        eventCallback(status);
    }
}

DownloadStatus HttpsDownload2::getStatus()
{
    return status;
}
uint8_t HttpsDownload2::getListSize()
{
    return downloadQueue.size();
}

size_t HttpsDownload2::getTotalSize()
{
    return totalSize;
}

size_t HttpsDownload2::getDownloadedSize()
{
    return downloadedSize;
}

float HttpsDownload2::getDownloadPercentage()
{
    if (totalSize == 0)
    {
        return 0;
    }
    return (float)downloadedSize / totalSize * 100;
}

void HttpsDownload2::updateSpeed()
{
    if (downloadedSize == 0 || startTime == 0)
    {
        averageSpeed = 0;
        return;
    }
    TickType_t elapsedTicks = xTaskGetTickCount() - startTime;
    float elapsedSeconds = (float)elapsedTicks / configTICK_RATE_HZ;
    averageSpeed = (float)downloadedSize / elapsedSeconds;
}

float HttpsDownload2::getAverageDownloadSpeed()
{
    return averageSpeed;
}
void HttpsDownload2::print()
{
    if(getStatus() == DownloadStatus::DOWNLOADING)
    {
        ESP_LOGI(TAG,"tasks[%u],file:[%s],(%zu/%zuBytes), speed:[%.3fKB/s]",
            getListSize() + 1,info.filePath.c_str(),downloadedSize,totalSize,averageSpeed/1024.0);
    }
    else
    {
        ESP_LOGI(TAG,"tasks[%u]",getListSize());

    }
}

HttpsDownload2 downloader;